package cas.ihep.hep.analysis;

import cas.ihep.util.MemoryBuffer;

import java.io.DataInput;
import java.io.IOException;
import java.util.List;

public class NewPWACalculator {
    static class Data{
        double []par;
        boolean isMc=false;
        static Data readFrom(DataInput is)throws IOException {
            Data ret=new Data();
            ret.par=new double[8];
            for(int i=0;i<8;i++){
                ret.par[i]=is.readDouble();
            }
            return ret;
        }
        static Data readFrom(MemoryBuffer buf){
            Data ret=new Data();
            ret.par=new double[8];
            for(int i=0;i<8;i++){
                ret.par[i]=buf.getDouble();
            }
            return ret;
        }
    }
    static class double2{
        double x,y;
        double2(){}
        double2(double _1,double _2){
            x=_1;
            y=_2;
        }
    }

    private static double2 multi0(double2 x1,double2 x2){
        return new double2(x1.x*x2.x + x1.y*x2.y,
                x1.y*x2.x - x1.x*x2.y);
    }

    private static double2 bw0(double mx2,double mr,double wr){
        double2 out=new double2();
        if (wr<10){
            double mr2 = mr*mr;
            double diff = mr2-mx2;
            double denom = diff*diff + wr*wr*mr2;
            if(wr<0){
                out.x=1/Math.sqrt(denom);
                out.y=0;
            }else{
                out.x=diff/denom;
                out.y=wr*mr/denom;
            }
        }else{
            out.x=1;
            out.y=0;
        }
        return out;
    }

    private static void FunUserParameterSpark0(double Mag_bw1, double Pha_bw1, double Mag_bw2, double Pha_bw2, double2[] out){
        double2 P_1=new double2(Mag_bw1*Math.cos(Pha_bw1),Mag_bw1*Math.sin(Pha_bw1));
        double2 P_2 = new double2(Mag_bw2*Math.cos(Pha_bw2),Mag_bw2*Math.sin(Pha_bw2));
        out[0]=multi0(P_1,P_1);
        out[1]=multi0(P_1,P_2);
        out[2]=multi0(P_2,P_2);
    }

    private static void FunCalculatorSpark0(double[] t, double M_bw1, double W_bw1, double M_bw2, double W_bw2, double2[] out){
        double p12x=t[0]+t[4],p12y=t[1]+t[5],p12z=t[2]+t[6],p12w=t[3]+t[7];
        double mx2 = p12w*p12w-(p12x*p12x+p12y*p12y+p12z*p12z);
        double2 U_BW1=bw0(mx2,M_bw1,W_bw1);
        double2 U_BW2=bw0(mx2,M_bw2,W_bw2);
        out[0]=multi0(U_BW1,U_BW1);
        out[1]=multi0(U_BW1,U_BW2);
        out[2]=multi0(U_BW2,U_BW2);
    }

    private static double getDcsSpark0(double2[] PAij, double2[] fun_ij){
        int _y;
        int ind=0;
        double tempsum=0;
        final int N_wave=2;
        ///const int N_ij=3;
        for(int _i=0; _i < N_wave; _i++){
            _y = ind;
            double2 tempval=multi0(PAij[_y],fun_ij[_y]);
            tempsum+=tempval.x*tempval.x;
            ind++;
            for(int _j=_i+1; _j < N_wave; _j++){
                _y=ind;
                tempsum+=2.0*(PAij[_y].x*fun_ij[_y].x-PAij[_y].y*fun_ij[_y].y);
                ind++;
            }
        }
        return tempsum;
    }

    private static void waitFor(int time){
//        long last=System.nanoTime();
//        while(true){
//            //noinspection StatementWithEmptyBody
//            for(int i=0;i<1000;i++){
//                ;
//                //empty loop
//            }
//            long current=System.nanoTime();
//            if(current-last>1000){
//                return ;
//            }
//            last=current;
//        }
        long sum=0;
        for(int i=0;i<time;i++){
            sum+=i;
        }
    }

    public static double computeMc(double[] param,double[] buf){
        double2[] presult=new double2[3];
        FunUserParameterSpark0(param[2],param[3],param[6],param[7],presult);
        double2[] result=new double2[3];
        FunCalculatorSpark0(buf,param[0],param[1],param[4],param[5],result);
        return getDcsSpark0(presult,result)*4;
    }

    public static double computeDt(double[] param,double[] buf){
        double2[] presult=new double2[3];
        FunUserParameterSpark0(param[2],param[3],param[6],param[7],presult);
        double2[] result=new double2[3];
        FunCalculatorSpark0(buf,param[0],param[1],param[4],param[5],result);
        return -Math.log(getDcsSpark0(presult,result)*4);
    }

    public static double[] compute(double[] param, List<Data> dt,long dtsize,List<Data> mc,long mcsize,int w){
        double2[] presult=new double2[3];
        FunUserParameterSpark0(param[2],param[3],param[6],param[7],presult);
        double2[] result=new double2[3];
        double ret0=0,ret1=0;
        for(int i=0;i<dtsize;i++){
            Data cur=dt.get(i);
            double[] buf=cur.par;
            FunCalculatorSpark0(buf,param[0],param[1],param[4],param[5],result);
            ret0-=Math.log(getDcsSpark0(presult,result)*4);
            if(w>0) {
                waitFor(w);
            }
        }
        for(int i=0;i<mcsize;i++){
            double[] buf=mc.get(i).par;
            FunCalculatorSpark0(buf,param[0],param[1],param[4],param[5],result);
            ret1+=getDcsSpark0(presult,result)*4;
            if(w>0) {
                waitFor(w);
            }
        }
        return new double[]{ret0,ret1};
    }

    public static double[] compute(double[] param, MemoryBuffer dt, long dtsize, MemoryBuffer mc, long mcsize,int w){
        double2[] presult=new double2[3];
        FunUserParameterSpark0(param[2],param[3],param[6],param[7],presult);
        double2[] result=new double2[3];
        double ret0=0,ret1=0;
        double[] buf=new double[8];
        for(int i=0;i<dtsize;i++){
            for(int j=0;j<8;j++){
                buf[j]=dt.getDouble();
            }
            FunCalculatorSpark0(buf,param[0],param[1],param[4],param[5],result);
            ret0-=Math.log(getDcsSpark0(presult,result)*4);
            if(w>0) {
                waitFor(w);
            }
        }
        for(int i=0;i<mcsize;i++){
            for(int j=0;j<8;j++){
                buf[j]=mc.getDouble();
            }
            FunCalculatorSpark0(buf,param[0],param[1],param[4],param[5],result);
            ret1+=getDcsSpark0(presult,result)*4;
            if(w>0) {
                waitFor(w);
            }
        }
        dt.reset();
        mc.reset();
        return new double[]{ret0,ret1};
    }
}
